# -*- encoding: utf-8 -*-
#
# by stockbsd@gmail.com
# 

from google.appengine.api import xmpp
from google.appengine.ext import db
from datetime import datetime,timedelta


class Cron(db.Model):
	user = db.StringProperty(required=True)
	expr = db.TextProperty(required=True)
	desc = db.TextProperty(required=True)
	enabled = db.BooleanProperty(required=True)

def crSchedule():
	dtnow = datetime.utcnow()
	query = db.GqlQuery('select * from Cron where enabled = True')
	for item in query:
		if matchTime(dtnow,item.expr) :
			xmpp.send_message( item.user,item.desc )

def crProc(sender,cmd):
	pcmd = cmd.strip()
	pos = pcmd.find(' ')
	
	verb = pcmd
	ar = ''
	if pos != -1 :
		verb = pcmd[:pos]
		ar = pcmd[pos+1:]

	if(verb=='-list'):
		crList(sender)
	elif(verb=='-del'):
		crDel(sender,ar)
	elif(verb=='-clear'):
		crClear(sender)
	elif(verb=='-add'):
		crAdd(sender,ar)
	elif(verb=='-start'):
		crEnable(sender,ar,True)
	elif(verb=='-stop'):
		crEnable(sender,ar,False)
	elif(verb=='-startall'):
		crEnable(sender,None,True)
	elif(verb=='-stopall'):
		crEnable(sender,None,False)
	else:
		crHelp(sender)

def crEnable(sender,target,en):
	query= db.GqlQuery('select * from Cron where user= :1 ',sender)
	for item in query:
		if target == None or str( item.key().id() ) == target:
			item.enabled = en
			item.put()
	xmpp.send_message(sender,'Done. \cron to see the result.')

def crAdd(sender,addCmd):
	fmtmsg = '''
		Each command has the format:		
		<minute> <hour> <day of month> <month> <day of week> <timezone> msg

		The entries for each of these values must be either:
		  - A number. For <month> it is the month number, and for <day of week> is the day number starting with Monday=0.
		  - An asterisk (*) which indicates that any value matches this field.
		  - Comma separated numbers that indicate that each of the values can match this field.
				
		Example cron entries
		  0 * * * * -2   "run every hour,timezone -2"
		  0 3 * * * -1   "run every day at 3am"
		  30,0 * * * * +8   "run every half hour"
		  45 15 * * 0 +5   "run every monday at 3:45pm"
		  0 4 15 * * -7   "run on the 15th of every month at 4am"
		'''

	maxspt = 6 # min hour day mon dow tz msg
	g = addCmd.split(' ',maxspt)
	if len(g) == maxspt+1:
		try:
			tz = int( g[maxspt-1] )	# may raise ValueError
			cr = Cron(user=sender,expr=addCmd[:addCmd.rfind(g[maxspt])-1],desc=g[maxspt],enabled=True)
			cr.put()

			xmpp.send_message(sender,'Item added :)')
		except ValueError:
			xmpp.send_message(sender,fmtmsg)
	else:
		xmpp.send_message(sender,fmtmsg)


def crDel(sender,id):
	query= db.GqlQuery('select * from Cron where user= :1 ',sender)
	for item in query:
		if str( item.key().id() ) == id :
			item.delete()
			xmpp.send_message(sender,'item deleted :)')
			return 
	xmpp.send_message(sender,'item not found :(')

def crList(sender):
	query= db.GqlQuery('select * from Cron where user= :1 ',sender)
	result = query.fetch(1000)
	msg = 'you have %d cron jobs (+ indicates running ,- stopped) :' % len(result)
	dictx = {True:'\n +[',False:'\n -['}
	for item in result:
		msg += dictx[item.enabled] + str(item.key().id()) + '] ' + item.expr + ' ' + item.desc
	xmpp.send_message(sender,msg)

def crClear(sender):
	query= db.GqlQuery('select * from Cron where user= :1 ',sender)
	for item in query:
		item.delete()
	xmpp.send_message(sender,'you have no cron jobs')

def crHelp(sender):
	msg = ''' cron has some subcmds:
			\cron or \cron -list
			\cron -add 30 8 * * 0 +8 every mondy 8:30 [timezone +8]
			\cron -del id
			\cron -start id
			\cron -stop id
			\cron -startall
			\cron -stopall
			\cron -clear
			\cron -help
			'''
	
	xmpp.send_message(sender,msg)

def match(value, expr):
	if expr == '*':
		return True
	values = expr.split(',')
	for v in values:
		rgs = v.split('-',1)
		if len(rgs) == 2 :
			if value in range( int(rgs[0]),int(rgs[1])+1 ) :
				return True
		elif int(v) == value:
			return True
	return False

def matchTime(dtt,dtexp):
	g = dtexp.split(' ',5)
	delta = timedelta(hours= int(g[5]))
	dt = dtt + delta	# don't modify dtt
	ret = match(dt.minute, g[0]) and match(dt.hour,g[1]) and match(dt.day,g[2]) and match(dt.month,g[3]) and match(dt.weekday(),g[4])
	return ret 
